﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using DomainDriver.DomainModeling.DataProviders;
using DomainDriver.DomainModeling.DomainObjects;
using DomainDriver.DomainModeling.DomainModels;
using DomainDriver.DomainModeling.Events;

namespace DomainDriver.DomainModeling.StorageManagers.DataSetStorage
{
    public class DataSetFileStorageManager<KEY, KEYED_DOMAIN_OBJECT> : DataSetFileStorageManager<KEY, KEYED_DOMAIN_OBJECT, DataSet, DataRow>
        where KEYED_DOMAIN_OBJECT : IKeyedDomainObject<KEY, KEYED_DOMAIN_OBJECT>, IDataSetSerializable<DataSet, DataRow>
    {
        //public DataSetFileStorageManager(IDataProvider dataProvider)
        //    : base(dataProvider)
        //{ }

        public DataSetFileStorageManager(IDataProvider dataProvider, Expression<Func<KEYED_DOMAIN_OBJECT>> constructionMethod, IBatchPersistableDomainModel batchPersistableDomainModel)
            : base(dataProvider, constructionMethod, batchPersistableDomainModel)
        { }
    }

    public class DataSetFileStorageManager<KEY, KEYED_DOMAIN_OBJECT, DATASET> : DataSetFileStorageManager<KEY, KEYED_DOMAIN_OBJECT, DATASET, DataRow>
        where KEYED_DOMAIN_OBJECT : IKeyedDomainObject<KEY, KEYED_DOMAIN_OBJECT>, IDataSetSerializable<DATASET, DataRow>
        where DATASET : DataSet
    {
        //public DataSetFileStorageManager(IDataProvider dataProvider)
        //    : base(dataProvider)
        //{ }

        public DataSetFileStorageManager(IDataProvider dataProvider, Expression<Func<KEYED_DOMAIN_OBJECT>> constructionMethod, IBatchPersistableDomainModel batchPersistableDomainModel)
            : base(dataProvider, constructionMethod, batchPersistableDomainModel)
        { }
    }

    public class DataSetFileStorageManager<KEY, KEYED_DOMAIN_OBJECT, DATASET, DATAROW> : TransientStorageManager<KEY, KEYED_DOMAIN_OBJECT>, IBatchPersistableStorageManager<KEY, KEYED_DOMAIN_OBJECT>
        where KEYED_DOMAIN_OBJECT : IKeyedDomainObject<KEY, KEYED_DOMAIN_OBJECT>, IDataSetSerializable<DATASET, DATAROW>
        where DATASET : DataSet
        where DATAROW : DataRow
    {
        private static Func<KEYED_DOMAIN_OBJECT> m_ConstructionDelegate = null;
        public static Func<KEYED_DOMAIN_OBJECT> ConstructionDelegate
        {
            get { return m_ConstructionDelegate; }
            set { m_ConstructionDelegate = value; }
        }

        protected string m_DomainObjectTableName;
        protected string m_DomainObjectKeyColumnName;

        //public DataSetFileStorageManager(IDataProvider dataProvider)
        //    : this(dataProvider, null)
        //{ }

        public DataSetFileStorageManager(IDataProvider dataProvider, Expression<Func<KEYED_DOMAIN_OBJECT>> constructionMethod, IBatchPersistableDomainModel batchPersistableDomainModel)
            : base(dataProvider)
        {
            if (constructionMethod != null)
            { m_ConstructionDelegate = constructionMethod.Compile(); }

            if (m_ConstructionDelegate == null)
            { throw new ApplicationException(); }

            KEYED_DOMAIN_OBJECT domainObject = m_ConstructionDelegate();

            m_DomainObjectTableName = domainObject.ObjectTableName;
            m_DomainObjectKeyColumnName = domainObject.KeyColumnName;

            batchPersistableDomainModel.PersistingToDataSource += PersistingToDataSource;
            batchPersistableDomainModel.LoadingFromDataSource += LoadingFromDataSource;
        }

        public string TableName
        {
            get { return m_DomainObjectTableName; }
        }

        public string KeyColumnName
        {
            get { return m_DomainObjectKeyColumnName; }
        }

        protected DATASET DataSource
        {
            get { return DataProvider.GetTypedDataSource<DATASET>(); }
        }

        public virtual void LoadingFromDataSource(IDomainModel sender, EventArgs args)
        {
            m_ObjectStore.Clear();

            foreach (DataRow domainObjectRow in DataSource.Tables[TableName].Select())
            {
                KEYED_DOMAIN_OBJECT domainObject = ConstructionDelegate();
                domainObject.Deserialize(DataSource, (domainObjectRow as DATAROW));
                m_ObjectStore.Add(domainObject.Key, domainObject);
            }
        }

        public virtual void PersistingToDataSource(IDomainModel sender, EventArgs args)
        {
            foreach (KEYED_DOMAIN_OBJECT domainObject in m_ObjectStore.Values)
            {
                DATAROW domainObjectRow = null;

                var matchingRows =
                    from row in DataSource.Tables[TableName].Select()
                    where domainObject.Key.Equals(row[KeyColumnName])
                    select row;

                if (matchingRows.Count<DataRow>() < 1)
                {
                    domainObjectRow = (DataSource.Tables[TableName].NewRow() as DATAROW);
                    domainObjectRow[KeyColumnName] = domainObject.Key;
                    DataSource.Tables[TableName].Rows.Add(domainObjectRow);
                    domainObject.Serialize(DataSource, domainObjectRow);
                }
                else
                {
                    domainObjectRow = (matchingRows.First<DataRow>() as DATAROW);
                }
            }
        }
    }
}